/**********************************/
/* Module R 5 - Serial Port
   Group 3
   Nov 11, 2008
   */
   /***********************************/

   #include <stdio.h>
   #include <string.h>
   #include <dos.h>
   #include "mpx_supt.h"
   #define RINGBUFFERSIZE 10

   /* define DCB structure */

   struct DCB{
	int state; // Allocation Status of the DCB
	int flag; //port flag
	int*  e_flag; //event flag
     char* in_buffer;  /* the buffer  */
     int* in_count;  /*  counter for number of bytesin the buffer */
      int in_done; /*counter for how many characters have been processed */
      char* out_buffer;  /* the buffer  */
      int* out_count;  /*  counter for number of bytes in the buffer */
      int out_done; /*counter for how many characters have been processed */
char  ringbuffer[RINGBUFFERSIZE];
int ringbuf_in;
int ringbuf_out;
int ringbuf_count;

} pDCB;

/*

static int tracker=0; //tracker counter

struct link {
   int value;
   struct link *next;
};

typedef struct link ring;

ring *RING_IN = NULL, *RING_OUT, *temp;


void rb_insert(int x)
{
	RING_IN = (ring *)malloc(sizeof(ring));
	RING_IN->value = x;
	if(tracker==0)
		RING_OUT = RING_IN;
	RING_IN = RING_IN->next;
	tracker++;
}

int rb_fetch()
{
	int val;
	if(tracker<=0)
	{
		//ERROR RING BUFFER EMPTY
	}
	else
	{
		val = RING_OUT->value;
		RING_OUT = RING_OUT->next;
		tracker--;
		return val;
	}
}

*/


//mpx_dcb pDCB
#define OPEN 1
#define CLOSE 0

#define PIC_MASK 0x21
#define PIC_CMD 0x20
#define EOI 0x20
#define COM1_INT_ID 0x0C
#define COM2_INT_ID 0x0B
#define COM1_BASE 0x3F8
#define COM2_BASE 0x2F8
#define COM1_INT_EN (COM1_BASE + 1)
#define COM1_BRD_LSB COM1_BASE
#define COM1_BRD_MSB (COM1_BASE + 1)
#define COM1_INT_ID_REG (COM1_BASE + 2)
#define COM1_LC (COM1_BASE + 3)

#define COM1_MC (COM1_BASE + 4)
#define COM1_LS (COM1_BASE + 5)
#define COM1_MS (COM1_BASE + 6)
int com_open(int *eflag_p, int baud_rate);
int com_close(void);
int com_read(char *buf_p, int *count_p);
int com_write(char
 *buf_p, int *count_p);
 void interrupt detectHandler();
 void inputHandler();
 void outputHandler();
 void interrupt (*oldHandler)(void);
 int com_open(int *eflag_p, int baud_rate)
 {        int mask;
	if(eflag_p == NULL)
	return -101;
	if(baud_rate <= 0)
	return -102;
	if(pDCB.flag==OPEN)
	return -103;
	else
	{    /* DCB initializtion */
	    int baud_rate_divisor;
	    pDCB.state = IDLE;
	    pDCB.flag = OPEN;
	    pDCB.e_flag= eflag_p;
	    pDCB.ringbuf_in=0;
	    pDCB.ringbuf_out=0;
	    pDCB.ringbuf_count=0;
	    oldHandler = getvect(COM1_INT_ID);
	    setvect (COM1_INT_ID,&detectHandler);
	    baud_rate_divisor = 115200/(long) baud_rate;
	    outportb(COM1_LC, 0x80);
	    outportb(COM1_BRD_LSB ,baud_rate_divisor & 0xFF);
	    outportb(COM1_BRD_MSB ,(baud_rate_divisor  >> 8) & 0xFF);
	    outportb(COM1_LC, 0x03);
	    disable();
	    mask = inportb(PIC_MASK);
	    mask = mask & ~0x10;
	    outportb(PIC_MASK, mask);
	    enable();
	    outportb(COM1_MC, 0x08);
	    outportb(COM1_INT_EN, 0x01);
	    //store 0x08 in modem control register to enable serial interrupt
	    //store 0x01 in the interrupt enable register to enable input ready interrupts
		return 0;
	}
}

int com_close(void)
{           int mask;
	if(pDCB
flag!= OPEN){
return -201;
}else{pDCB.flag=CLOSE;
disable();
mask = inportb(PIC_MASK);
mask = mask | 0x10;
outportb(PIC_MASK, mask);
enable();
//load 0
x00 into modem status and interrupt inable registers, disable ACC interrupts
//restore original interrupts using setvect()
outportb(COM1_MS, 0x00);
outportb(COM1_INT_EN, 0x00);
setvect (COM1_INT_ID,oldHandler);
return 0;
}
}


int com_read(char *buf_p, int *count_p)
{
if(pDCB.flag != OPEN)
	{
	return -301;
	}

	       if(buf_p == NULL)
		{
		return -302;
		   }
		if(count_p == NULL)
		{
		return -303;
		}
		if(pDCB.state != IDLE)
		{
		return -304;
		}
		else
		{
		pDCB.in_buffer = buf_p;
		pDCB.in_count = count_p;
		pDCB.in_done = 0;
		*pDCB.e_flag = 0;
		disable();
		pDCB.state = READ;
		//get chars from ring buffer
		//re-enable interrupts
  while ( ( pDCB.ringbuf_count>0)||(pDCB.in_
done<*pDCB.in_count)||(*(pDCB.in_buffer-1)!='\r') )
{
*pDCB.in_buffer = pDCB.ringbuffer[pDCB.ringbuf_out];
//pDCB.in_buffer = (pDCB *)malloc(sizeof(pDCB));
//pDCB.ringbuffer[pDCB.ringbuf_out] = rb_fetch();
pDCB.in_buffer++;
pDCB.in_done++;
pDCB.ringbuf_out = ( pDCB.ringbuf_out+1)%RINGBUFFERSIZE;
pDCB.ringbuf_count--;
}
  //
enable();
if(pDCB.in_done < *pDCB.in_count)
{
return 0;
}
if (*(pDCB.in_buffer-1) =='\r')
	   {
	   *(pDCB.in_buffer-1) = '\0';
	   }
	   else
	   {
	   *(pDCB.in_buffer) = '\0';
	   }
	   pDCB.state = IDLE;

	   *pDCB.e_flag= 1;
	   *pDCB.in_count = pDCB.in_done;
	   }
	   return 0;
	   }



int com_write(char *buf_p, int *count_p)
	   {
		 int mask;
		 if(pDCB.flag != OPEN)
		 {
		 return -401;
		 }
		 if(buf_p == NULL)
		{
		return -402;
		}
		//if((count_p == NULL)||(*count_p<=0))
		if(count_p == NULL)
		{
		return -403;
		}
		if(pDCB.state != IDLE)
		{
		return -404;
		}
		else
		{
		pDCB.out_buffer = buf_p;
		pDCB.out_count = count_p;
		pDCB.out_done =0;
		pDCB.state = WRITE;
		*pDCB.e_flag = 0;
		//outportb(COM1_BASE, rb_insert());
		outportb(COM1_BASE, *pDCB.out_buffer);
		pDCB.out_buffer++;
		pDCB.out_done++;
		disable();
		mask = inportb(COM1_INT_EN);
		mask = mask | 0x02;
		outportb(COM1_INT_EN,mask);
		enable();
		return 0;
		}
		}
		/*****************************************************
		Function:   void interrupt detectHandler()
		Outline:  If port is not open- clear the interrupt and return
outportb(0x20, EOI);  EOI = End of interrupt code
		Otherwise, read the Interrupt ID Register to determine the cause of the interrupt.
		Get the value of the Interrupt ID Register by using inportb(INT_ID);
		This will return an integer value.
		Bit 0 must be 0 if the interrupt was actually caused by the serial port.
		Bits 2 and 1 indicate the specific interrupt type:
		0 = Modem Status Interrupt
		01 = Output Interrupt
		10  = Input Interrupt
		11 = Line Status Interrupt
		Call the appropriate second-level handler
		If bit2bit1 = 01, call the write second level interrupt handler
If bit2bit1 = 10, call the read second level interrupt handler
		Clear the interrupt by sending EOI to the PIC Command Register (0x20)*******************************************************/
		void interrupt detectHandler()
		{
		int mask;
		int i;
		if(pDCB.flag !=OPEN)
		{
		outportb(PIC_CMD,EOI);
		return;
		}
		(int)i = inportb(COM1_INT_ID_REG); /*  read interrupt ID */



		if ((i&0x07)==0x00)
		inportb(COM1_MS);
		else if ((i&0x07)==0x02)
		outputHandler();
		else if ((i&0x07)==0x04)
		input Handler();
		else if ((i&0x07)==0x06)
		inportb(COM1_LS);
		outportb(PIC_CMD,EOI);
		}
		/**************************************
		Function:  void inputHandler()
		Outline:
		Read a character from the input regster
		Your variable = inportb(INPUT_REGISTER); Input register for COM1 is the base address for the port.
		If current status is NOT reading
Store the character in the ring buffer.
		If the buffer is full, discard the character.
		Return and do not signal completion.c If current status is reading
		Store the character in the input buffer
		If the count is not completed and the character is not a CRReturn and do not signal completion.
		Otherwise, transfer is complted.
		Set status to idle
Set the event flag (set = 1)
		Set input count = input done
		***************************************/
		void inputHandler()
		{
		char c;
		(char) c = inportb(COM1_BASE);
		if(pDCB.state!=READ)
		{
		if(pDCB.ringbuf_count==RINGBUFFERSIZE)
		return;
		else
		{
		pDCB.ringbuffer[pDCB.ringbuf_in]=c;
		pDCB.ringbuf_in =  (pDCB.ringbuf_in+1)%RINGBUFFERSIZE;
		pDCB.ringbuf_count++;
		}
		}
		else if (pDCB.state==READ)
		{*pDCB.in_buffer=c;
		 pDCB.in_buffer++;
		 //pDCB.in_done++;
		   pDCB.in_count++;
		 } else if  ((c=='\r')||(pDCB.in_done>*pDCB.in_count))
		 {
		 if (*(pDCB.in_buffer-1)=='\r')
		  *(pDCB.in_buffer-1)='\0';
		  else
		  *pDCB.in_buffer='\0';
		  }
		  pDCB.state= IDLE;
		  *pDCB.e_flag=1;
		  *(pDCB.in_count) = pDCB.in_done;
		  }
		  /*************************************
		  Function:  void outputHandler()
		  Outline:
		  If the current status is NOT writing
		  Ignore the interrupt and return.
		  If count has not been exhausted
		  Get the next character from the output buffer
		  Store it in the output register.
		  Return and do not signal completion.
		  Otherwise, all characters have been transferred.
		  Reset the status to idle.
		  Set the event  flag (=1)
		  Set the count value.
		  Disable write interrupts by clearing bit 1 in the interrupt enable register.
		  mask = inportb(INTERRUPT_ENABLE);
		  mask = mask & ~0x02;
		  outportb(INTERRUPT_ENABLE,mask);
		  ************************************/

		  void outputHandler(){
		  unsigned char input;
		  int mask;
		  if(pDCB.state!=WRITE)
		  return;
		  if(pDCB.out_done<pDCB.out_count)
		  {
		  outportb(COM1_BASE, *pDCB.out_buffer);
		  (*pDCB.out_count)++;
		  pDCB.out_done++;
		  return;
		  }
		  else
		  {
		  pDCB.state=IDLE;
		  *pDCB.e_flag=1;
		  disable();
		  mask = inportb(COM1_INT_EN);// define INTERRUPT_ENABLE later
		  mask = mask & ~0x02;
		  outportb(COM1_BASE + 1,mask);
		  enable();
		  //outportb(PIC_CMD,EOI);
		  }
		  }